home *** CD-ROM | disk | FTP | other *** search
- Unit KeybFAQ;
- (* This is version 0.90 of KEYBFAQ, a Unit that answers two questions
- * often asked in the Pascal message area's:
- * - How do I change my cursor ?
- * - How can I perform input of String With certain limitations
- * (such as 'maximum length', 'only numbers' etc.)
- *
- * I will distribute this Unit *ONCE* in message form (three messages)
- * because it takes up 500 lines of code. It is untested code, cut from
- * my Unit library, and distributed *as is* With no other documentation
- * than these initial lines. You can use this code in your apps as you like,
- * and you can redistribute it, provided you:
- * - redistribute *source* code;
- * - do not Charge anything For the source code;
- * - give me credit For the original code if you change anything;
- * - keep this 'documentation' With it.
- * (Loosely translated: common decency is enough)
- * Copyright will formally remain mine.
- *
- * Please do not respond about this code. I am going away For a few weeks
- * and will distribute version 1.0 in ZIP form after that. That package
- * will have *tested* code, docs and examples.
- *
- * Some notes about this code:
- * - Use it always, or don't use it. I.e. if you start using GetKey
- * you should use that throughout your Program, and drop all ReadKeys.
- * - The redefinition of Char into Key has two reasons:
- * - it allows better Type checking
- * - it allows future changes to the internal representation of the
- * Key Type (I plan to make it a Word Type to handle the overlap
- * in key definitions that is still present, and/or adapt Unicode
- * Character definitions)
- * - The overlap in the Constant key definitions may look
- * problematic, but in the years I have been using this, it has not
- * posed any problems, generally because you only allow those keys
- * that have a meaning For your app.
- *
- * Happy Pascalling,
- * Jan Doggen, 27/8/93 *)
-
- Interface
-
- Type
- Key = Char;
- KeySet = Set of Key;
- (* See later in this Interface section For defined sets *)
-
- Var
- BlankChar : Char; (* Char used by GetStr to fill the bar; default ' ' *)
-
- Procedure FlushKeyBuf;
- (* Clears the BIOS keyboard buffer *)
-
- Function InsertStatus : Boolean;
- Procedure SetInsertStatus(On : Boolean);
-
- Procedure NiceBeep;
- (* Replaces the system beep With a more pleasant one. *)
-
- Type
- CursType = (NOCUR, LINECUR, BLOCKCUR);
-
- Procedure SetCursor(CType: CursType);
- (* SetCursor sets a block or line cursor, or no cursor. *)
-
- Function GetVidMode : Byte;
- (* Return BIOS video mode *)
-
- Function MonoChrome(Vmode : Byte) : Boolean;
- (* Returns True if a monochrome video mode is specified *)
-
- Function WinLeft : Byte;
- Function WinRight : Byte;
- Function WinTop : Byte;
- Function WinBottom : Byte;
- (* Return Absolute co-ordinates of current Window *)
-
- Function RepeatStr(Str : String; N : Integer) : String;
- (* Returns a String consisting of <N> repetitionsof <Str>. *)
-
- Function GetKey : Key;
- (* Returns a Variable of Type Key; see the table below For the definitions.
- * GetKey also accepts the <Alt-numeric keypad> ASCII codes. *)
-
- Var
- ClearOnFirstChar,
- WalkOut,
- StartInFront : Boolean;
- (* These Booleans influence the way in which GetStr operates:
- *
- * With WalkOut = True: the left and right arrow keys also act as ExitKeys
- * when they bring us 'outside' of the Word (we Exit the Procedure).
- *
- * With ClearOnFirstChar = True: if the first key Typed is a Character,
- * the initial Str is cleared.
- *
- * With StartInFront = True: the cursor will be positioned at the first
- * Character when we start the Procedure (instead of after the last)
- *
- * Default settings For these Booleans are False. *)
-
- Procedure GetStr(Xpos, Ypos,
- MaxLen,
- Ink, Paper : Byte;
- AllowedKeys,
- ExitKeys : KeySet;
- BeepOnError : Boolean;
- Var Str : String;
- Var ExitKey : Key);
- (* Reads a String of max. <MaxLen> Characters starting at relative position
- * <XPos,YPos>. A bar of length <MaxLen> is placed there With colors
- * <Ink> on <Paper>. An initial value For the String returned can be
- * passed With <Str>.
- *
- * - BeepOnError indicates audio feedback on incorrect keypresses
- * - AllowedKeys is a set of Keys that may be entered. if AllowedKeys = [],
- * all keys are allowed.
- * - ExitKeys is a set of Keys that stop the Procedure; <Str> will then
- * contain the edited String and <ExitKey> will be key that made us Exit.
- * if ExitKeys is [], it will be replaced by [Enter,Escape].
- * The keys you specify in ExitKeys, do not have to be specified in
- * AllowedKeys. *)
-
- Function WaitKey(LegalKeys : Keyset; Flush : Boolean) : Key;
- (* Waits For one of the keys in LegalKeys to be pressed, then returns this.
- * if <Flush> = True, the keyboard buffer is flushed first. *)
-
- Const
- Null = #0; CtrlA = #1; F1 = #187; Home = #199;
- BSpace = #8; CtrlB = #2; F2 = #188; endKey = #207;
- Tab = #9; CtrlC = #3; F3 = #189; PgUp = #201;
- Lfeed = #10; CtrlD = #4; F4 = #190; PgDn = #209;
- Ffeed = #12; CtrlE = #5; F5 = #191; Left = #203;
- CReturn = #13; CtrlF = #6; F6 = #192; Right = #205;
- Escape = #27; CtrlG = #7; F7 = #193; Up = #200;
- ShiftTab = #143; CtrlH = #8; F8 = #194; Down = #208;
- CtrlPrtsc = #242; CtrlI = #9; F9 = #195; Ins = #210;
- Enter = #13; CtrlJ = #10; F10 = #196; Del = #211;
- Esc = #27; CtrlK = #11; ShiftF1 = #212; CtrlLeft = #243;
- Space = #32; CtrlL = #12; ShiftF2 = #213; CtrlRight = #244;
- CtrlM = #13; ShiftF3 = #214; CtrlendKey = #245;
- { Note the } CtrlN = #14; ShiftF4 = #215; CtrlPgdn = #246;
- { overlap of } CtrlO = #15; ShiftF5 = #216; CtrlPgup = #127;
- { Ctrl-keys } CtrlP = #16; ShiftF6 = #217; CtrlHome = #247;
- { and others ! } CtrlQ = #17; ShiftF7 = #218;
- CtrlR = #18; ShiftF8 = #219;
- CtrlS = #19; ShiftF9 = #220;
- CtrlT = #20; ShiftF10 = #221;
- CtrlU = #21; CtrlF1 = #222;
- CtrlV = #22; CtrlF2 = #223;
- CtrlW = #23; CtrlF3 = #224;
- CtrlX = #24; CtrlF4 = #225;
- CtrlY = #25; CtrlF5 = #226;
- CtrlZ = #26; CtrlF6 = #227;
- AltQ = #144; CtrlF7 = #228;
- AltW = #145; CtrlF8 = #229;
- AltE = #146; CtrlF9 = #230;
- AltR = #147; CtrlF10 = #231;
- AltT = #148; AltF1 = #232;
- AltY = #149; AltF2 = #233;
- AltU = #150; AltF3 = #234;
- AltI = #151; AltF4 = #235;
- AltO = #152; AltF5 = #236;
- AltP = #153; AltF6 = #237;
- AltA = #158; AltF7 = #238;
- AltS = #159; AltF8 = #239;
- AltD = #160; AltF9 = #240;
- AltF = #161; AltF10 = #241;
- AltG = #162;
- AltH = #163;
- AltJ = #164;
- AltK = #165;
- AltL = #166; Alt1 = #248;
- AltZ = #172; Alt2 = #249;
- AltX = #173; Alt3 = #250;
- AltC = #174; Alt4 = #251;
- AltV = #175; Alt5 = #252;
- AltB = #176; Alt6 = #253;
- AltN = #177; Alt7 = #254;
- AltM = #178; Alt8 = #255; { No Alt9 or Alt0 ! }
-
- { SETS }
- LetterKeys : KeySet = ['A'..'Z','a'..'z'];
- SpecialKeys : KeySet =
- ['!','?','b','a','a','a','a','a','A','a','A','A','e','e','e',
- 'e','E','i','i','i','i','o','o','o','o','o','O','u','u','u',
- 'u','U','c','C','n','N'];
- UpKeys : KeySet = ['A'..'Z'];
- LowKeys : KeySet = ['a'..'z'];
- VowelKeys : KeySet = ['a','e','i','o','u','A','E','I','O','U'];
- DigitKeys : KeySet = ['0'..'9'];
- OperatorKeys : KeySet = ['*','/','+','-'];
- YNKeys : KeySet = ['y','n','Y','N'];
- JNKeys : KeySet = ['j','n','J','N'];
- BlankKeys : KeySet = [#0..#32];
- AllKeys : KeySet = [#0..#255];
- FKeys : KeySet = [F1..F10];
- ShiftFKeys : KeySet = [ShiftF1..ShiftF10];
- AltFKeys : KeySet = [AltF1..AltF10];
- CtrlFKeys : KeySet = [CtrlF1..CtrlF10];
- AllFKeys : KeySet = [F1..F10,ShiftF1..AltF10];
-
- Implementation
-
- Uses Crt,Dos;
-
- Procedure NiceBeep; (* Replaces the system beep With a more pleasant one. *)
- begin
- Sound(300);
- Delay(15);
- NoSound;
- end;
-
-
- Procedure FlushKeyBuf;
- Var
- Ch : Char;
- begin
- While KeyPressed do
- Ch := ReadKey;
- end;
-
-
- Function InsertStatus : Boolean;
- Var
- Regs : Registers;
- begin
- Regs.AH := 2;
- Intr($16, Regs);
- InsertStatus := ((Regs.AL and 128) = 128);
- end;
-
-
- Procedure SetInsertStatus(On: Boolean);
- begin
- if ON then
- Mem[$0040:$0017] := Mem[$0040:$0017] or 128
- else
- Mem[$0040:$0017] := Mem[$0040:$0017] and 127;
- end;
-
-
- Function GetVidMode: Byte;
- Var
- Regs : Registers;
- begin
- Regs.AH := $0F;
- Intr($10, Regs);
- GetVidMode := Regs.AL;
- end;
-
-
- Function MonoChrome(Vmode : Byte) : Boolean;
- begin
- MonoChrome := (VMode in [0,2,5,6,7,15,17]);
- end;
-
-
- Function WinLeft : Byte;
- begin
- WinLeft := Lo(WindMin) + 1;
- end;
-
-
- Function WinRight : Byte;
- begin
- WinRight := Lo(WindMax) + 1;
- end;
-
-
- Function WinTop : Byte;
- begin
- WinTop := Hi(WindMin) + 1;
- end;
-
-
- Function WinBottom : Byte;
- begin
- WinBottom := Hi(WindMax) + 1;
- end;
-
-
- Function RepeatStr(Str : String; N : Integer) : String;
- Var
- Result : String;
- I, J,
- NewLen,
- Len : Integer;
- begin
- Len := Length(Str);
- NewLen := N * Length(Str);
- Result[0] := Chr(NewLen);
- J := 1;
- For I := 1 to N DO
- begin
- Move(Str[1], Result[J], Len);
- Inc(J, Len);
- end;
- RepeatStr := Result;
- end;
-
-
- Procedure SetCursor(CType : CursType);
- Var
- VM : Byte;
- Regs : Registers;
- begin
- VM := GetVidMode;
- With Regs DO
- Case CType OF
- NOCUR :
- begin
- Regs.CX := $2000; { Off-screen cursor position }
- Regs.AH := 1;
- end;
-
- LINECUR : begin
- AX := $0100;
- BX := $0000;
- if MonoChrome(VM) then
- CX := $0B0C
- else
- CX := $0607
- end;
-
- BLOCKCUR :
- begin
- AX := $0100;
- BX := $0000;
- if MonoChrome(VM) then
- CX := $010D
- else
- CX := $0107;
- end;
- end;
- Intr($10, Regs);
- end;
-
-
- Function GetKey : Key;
- Var
- Ch : Char;
- begin
- Ch := ReadKey;
- if Ch = #0 then
- begin
- Ch := ReadKey;
- if Ch <= #127 then
- GetKey := Chr(Ord(Ch) or $80)
- else
- if Ch = #132 then
- GetKey := CtrlPgUp
- else
- GetKey := Null;
- end
- else
- GetKey := Ch;
- end;
-
- Procedure GetStr(XPos, YPos, MaxLen, Ink, Paper : Byte; AllowedKeys,
- ExitKeys : KeySet; BeepOnError : Boolean;
- Var Str : String; Var ExitKey : Key);
- Var
- CursPos,
- LeftPos,
- TopPos,
- RightPos,
- BottomPos,
- X, Y : ShortInt;
- InsFlag,
- OAFlag,
- FirstKey : Boolean;
- InKey : Key;
- OldTextAttr : Byte;
- OldWindMin,
- OldWindMax : Word;
-
- Procedure CleanUp;
- { Second level; called when we leave }
- begin
- WindMin := OldWindMin;
- WindMax := OldWindMax;
- TextAttr := OldTextAttr;
- ExitKey := InKey;
- end;
-
- begin
- LeftPos := WinLeft;
- RightPos := WinRight;
- TopPos := WinTop;
- BottomPos := WinBottom;
- X := XPos + LeftPos - 1;
- Y := YPos + TopPos - 1;
- InsFlag := InsertStatus;
- if ExitKeys = [] then
- ExitKeys := [Enter, Escape];
- if AllowedKeys = [] then
- AllowedKeys := AllKeys;
- (* Save old settings here; restore them in proc CleanUp when Exiting *)
- OldWindMin := WindMin;
- OldWindMax := WindMax;
- WindMin := 0; { Set Absolute Window co-ordinates and }
- WindMax := $FFFF; { prevent scroll at lower right Character. }
- OldTextAttr := TextAttr;
- TextAttr := ((Paper SHL 4) or Ink) and $7F;
- { Note: the 'AND $F' ensures that blink is off }
- if StartInFront then
- CursPos := 1
- else
- if Length(Str)+1 < MaxLen then
- CursPos := Length(Str) + 1
- else
- CursPos := MaxLen;
- FirstKey := True;
- if InsFlag then
- SetCursor(BLOCKCUR)
- else
- SetCursor(LINECUR);
- Repeat
- if CursPos < 1 then
- if WalkOut then
- begin
- CleanUp;
- Exit;
- end
- else
- if BeepOnError then
- begin
- NiceBeep;
- CursPos := 1;
- end;
-
- if (CursPos > Length(Str) + 1) then
- if WalkOut then
- begin
- CleanUp;
- Exit;
- end
- else
- if BeepOnError then
- begin
- NiceBeep;
- CursPos := Length(Str) + 1;
- end;
-
- if CursPos > MaxLen then
- if WalkOut and (InKey = Right) then
- begin
- CleanUp;
- Exit;
- end
- else
- begin
- if BeepOnError then
- NiceBeep;
- CursPos := MaxLen;
- end;
-
- GotoXY(X, Y);
- Write(Str + RepeatStr(BlankChar, MaxLen - Length(Str)));
- GotoXY(X + CursPos - 1, Y);
- InKey := GetKey;
-
- if InKey in ExitKeys then
- begin
- CleanUp;
- Exit;
- end;
-
- Case InKey OF
- Left : Dec(CursPos);
- Right : Inc(CursPos);
- CtrlLeft, Home : CursPos := 1;
- CtrlRight, endKey : CursPos := Length(Str) + 1;
- Tab : Inc(CursPos,8);
- ShiftTab : Dec(CursPos,8);
-
- Ins :
- begin
- InsFlag := not InsFlag;
- if InsFlag then
- SetCursor(BLOCKCUR)
- else
- SetCursor(LINECUR);
- end;
-
- Del :
- if CursPos > Length(Str) then
- begin
- if BeepOnError then
- NiceBeep;
- end
- else
- Delete(Str, CursPos, 1);
-
- BSpace :
- if CursPos = 1 then
- if Length(Str) = 1 then
- Str := ''
- else
- begin
- if BeepOnError then
- NiceBeep;
- end
- else
- begin
- Delete(Str, CursPos - 1, 1);
- Dec(CursPos);
- end;
- else
- begin
- (* Note that 'AllowedKeys' that also have a
- * meaning as a control key have already been
- * processed, so they will not be handled here. *)
- if InKey in AllowedKeys then
- begin
- if ClearOnFirstChar and FirstKey then
- begin
- Str := '';
- CursPos := 1;
- end;
- if (CursPos = MaxLen) then
- begin
- Str[CursPos] := InKey;
- Str[0] := Chr(MaxLen);
- end
- else
- if InsFlag then
- begin
- Insert(InKey,Str,CursPos);
- if Length(Str) > MaxLen then
- Str[0] := Chr(MaxLen);
- end
- else
- begin
- Str[CursPos] := InKey;
- if CursPos > Length(Str) then
- Str[0] := Chr(CursPos);
- end;
-
- Inc(CursPos);
- end
- else
- if BeepOnError then
- NiceBeep;
- end;
- end;
-
- FirstKey := False;
- Until 0 = 1;
- end;
-
-
- Function WaitKey(LegalKeys : Keyset; Flush : Boolean) : Key;
- Var
- K : Key;
- begin
- if Flush then
- FlushKeybuf;
- Repeat
- K := GetKey;
- Until K in LegalKeys;
- WaitKey := K;
- end;
-
-
- begin
- BlankChar := ' ';
- WalkOut := False;
- ClearOnFirstChar := False;
- StartInFront := False;
- end.